using System;
using System.Collections;
using System.Text;
using Sedna.Core.Persistence;
using Sedna.Core.Support;
using Iesi.Collections;
using Spring.Util;

namespace Sedna.Util
{
    public class MultiSortableCollectionProvider : IDataProvider
    {
        private IList sortInfo = new ArrayList();
        private int amount;
        private int startIndex;
        private ICollection data;
        private string sortExpression;
        private bool isAsc;
        private bool accumulateSortInfo;

        # region Constructors
        
        public MultiSortableCollectionProvider(ICollection data)
        {
            this.data = data;
            amount = data.Count;
        }

        public MultiSortableCollectionProvider(ICollection data, IList sortInfo)
            : this(data)
        {
            AddSortInfo(sortInfo);
        }
        
        # endregion  
        
        # region Properties

        public bool IsAsc
        {
            get { return isAsc; }
            set
            {
                if (isAsc == value)
                    return;
                
                isAsc = value;

                if (string.IsNullOrEmpty(sortExpression))
                    return;

                SortInfo si = GetSortInfo(sortExpression);
                
                if (si != null)
                    si.IsAsc = value;
            }
        }

        public string SortExpression
        {
            get { return sortExpression; }
            set
            {
                if (sortExpression == value)
                    return;

                if (string.IsNullOrEmpty(value))
                {
                    RemoveTemporarySortInfo();
                    return;
                }

                if (!accumulateSortInfo && !string.IsNullOrEmpty(sortExpression))
                {
                    SortInfo oldSI = GetSortInfo(sortExpression);

                    if (oldSI != null && oldSI.IsTemporary)
                        RemoveSortInfo(oldSI);
                }

                if (!ContainsSortInfo(value))
                    AddSortInfo(new SortInfo(value, true));
                                
                sortExpression = value;
            }
        }

        public int StartIndex
        {
            get { return startIndex; }
            set { startIndex = value; }
        }

        public int TotalAmount
        {
            get { return data.Count; }
        }

        public IList SortInfo
        {
            get { return new ArrayList(sortInfo); }
        }

        public int Amount
        {
            get { return amount; }
            set { amount = value; }
        }        

        public bool AccumulateSortInfo
        {
            get { return accumulateSortInfo; }
            set { accumulateSortInfo = value; }
        }

        # endregion

        # region Manage SortInfo
        
        public bool ContainsSortInfo(SortInfo si)
        {
            Assert.IsNotNull(si, "SortInfo is null");

            return ContainsSortInfo(si.SortField);
        }
        
        public bool ContainsSortInfo(string sortField)
        {
            Assert.IsFalse(string.IsNullOrEmpty(sortField), "SortField is null or empty");

            foreach (SortInfo info in sortInfo)
            {
                if (info.SortField.Equals(sortField))
                    return true;
            }

            return false;
        }

        public bool IsFirstSortInfo(SortInfo si)
        {
            Assert.IsNotNull(si, "SortInfo is null");

            return IsFirstSortInfo(si);
        }

        public bool IsFirstSortInfo(string sortField)
        {
            Assert.IsFalse(string.IsNullOrEmpty(sortField), "SortField is null or empty");

            return (sortInfo.Count > 0 && (sortInfo[0] as SortInfo).SortField.Equals(sortField));
        }
        
        
        /// <summary>
        /// This method should not be public to prevent modification
        /// of SortInfo outside.
        /// </summary>
        private SortInfo GetSortInfo(string sortField)
        {
            Assert.IsFalse(string.IsNullOrEmpty(sortField), "SortField is null or empty");

            foreach (SortInfo info in sortInfo)
            {
                if (info.SortField.Equals(sortField))
                    return info;
            }

            return null;
        }
        
        public int GetSortInfoIndex(SortInfo si)
        {
            Assert.IsNotNull(si, "SortInfo is null");

            return GetSortInfoIndex(si.SortField);
        }

        public int GetSortInfoIndex(string sortField)
        {
            Assert.IsFalse(string.IsNullOrEmpty(sortField), "SortField is null or empty");

            SortInfo si = GetSortInfo(sortField);

            return (si != null) ? sortInfo.IndexOf(si) : -1;
        }
        
        public MultiSortableCollectionProvider AddSortInfo(IList sortInfo)
        {
            Assert.IsNotNull(sortInfo, "SortInfo is null");
            
            foreach (SortInfo si in sortInfo)
            {
                AddSortInfo(si);
            }
            
            return this;
        }
        
        public MultiSortableCollectionProvider AddSortInfo(SortInfo si)
        {
            Assert.IsNotNull(si, "SortInfo is null");
            Assert.IsFalse(ContainsSortInfo(si), "Such sort info already exists");

            sortInfo.Add(si);

            if (sortInfo.Count == 1)
                SetMainSortExpression();

            return this;
        }

        public MultiSortableCollectionProvider AddSortInfoFirst(SortInfo si)
        {
            Assert.IsNotNull(si, "SortInfo is null");

            if (!IsFirstSortInfo(si))
            {
                RemoveSortInfo(si);

                sortInfo.Insert(0, si);

                SetMainSortExpression();
            }

            return this;
        }

        public MultiSortableCollectionProvider RemoveSortInfo(SortInfo si)
        {
            Assert.IsNotNull(si, "SortInfo is null");

            int index = GetSortInfoIndex(si);
            
            if (index >= 0)
            {
                sortInfo.RemoveAt(index);
                
                if (index == 0)
                    SetMainSortExpression();
            }
            
            return this;
        }
        
        public void RemoveAllSortInfo()
        {
            sortInfo.Clear();
            SetMainSortExpression();
        }

        public void RemoveTemporarySortInfo()
        {
            for (int i = 0; i < sortInfo.Count; i++)
            {
                if ((sortInfo[i] as SortInfo).IsTemporary)
                    sortInfo.RemoveAt(i);
            }

            SetMainSortExpression();
        }
        
        private void SetMainSortExpression()
        {
            if (sortInfo.Count > 0)
            {
                // Must be set exactly the first SortInfo
                // 'cos when comparer sorts it uses the first SortInfo
                // as a main sort condition

                SortInfo si = this.sortInfo[0] as SortInfo;
                sortExpression = si.SortField;
                isAsc = si.IsAsc;
            }
            else 
                sortExpression = null;
        }

        # endregion

        public ICollection LoadData()
        {
            ArrayList result = new ArrayList(data);
            if (sortInfo != null && sortInfo.Count > 0)
            {
                result.Sort(new MultiplePropertyComparer(sortInfo) );
            }
            
            if (StartIndex < 0 || Amount < 0)
            {
                return result;
            }
            
            if (StartIndex < result.Count)
            {
                return result.GetRange(StartIndex, Math.Min(result.Count - StartIndex, Amount));
            }

            return Collections.EMPTY_LIST;
        }        

        public object Clone()
        {
            IDataProvider result = new MultiSortableCollectionProvider(data, sortInfo);
            result.StartIndex = StartIndex;
            result.Amount = Amount;

            return result;
        }
    }
}
